#pragma once

#include "game.h"
#include "game_entity.h"
#include "game_entity_list.h"
#include "game_chunk.h"
#include <assert.h>
#include <list>
#include "item.h"
#include "game_graphics.h"
#include "MessageRouter.h"

//#include "game_entity_list.h"


//Game singleton
static Game * _game = 0;
Game * game_get(){
	if (!_game){_game = new Game();}
	return _game;}

V2i game_input_get_mouse_pos()
{
	int x,y;
	SDL_GetMouseState(&x,&y);
	V2i Result = {x,y};
	return Result;
}

//move this code into seperate file
GameInput game_input_get_buttons(SDL_Event * _event, GameInput * last_input)
{
	assert(last_input);

	if (last_input)
	{
		for (int input_type = 0; input_type < GAME_KEY_FLAG_COUNT; input_type++)
		{
			if (last_input)
			{
				last_input->pressed[input_type] = false;
				last_input->released[input_type] = false;
			}
		}

		while(SDL_PollEvent(_event))
		{
			Uint32 event_type = _event->type; 
			if (event_type== SDL_QUIT){last_input->quit = true;}
			else if(event_type ==SDL_KEYDOWN)
			{
				//last_input->pressed = true;
				//printf("key pressed \n");
				switch(_event->key.keysym.sym)
				{
				case SDLK_LEFT:	{
					last_input->down[KEY_LEFT] = true;
					last_input->pressed[KEY_LEFT] = true;} break;

				case SDLK_RIGHT:{
					last_input->down[KEY_RIGHT] = true;
					last_input->pressed[KEY_RIGHT] = true;} break;

				case SDLK_UP:	{
					last_input->down[KEY_UP]= true;
					last_input->pressed[KEY_UP] = true;} break;

				case SDLK_DOWN:	{
					last_input->down[KEY_DOWN] = true;
					last_input->pressed[KEY_DOWN] = true;} break;

				case SDLK_SPACE:{
					last_input->down[KEY_SPACE] = true;
					last_input->pressed[KEY_SPACE] = true;} break;

				case SDLK_RETURN:{
					last_input->down[KEY_ENTER]= true;
					last_input->pressed[KEY_ENTER] = true;} break;

				case SDLK_ESCAPE:{
					last_input->down[KEY_EXIT] = true;
					last_input->pressed[KEY_EXIT] = true;} break;
				}
			}
			else if (event_type == SDL_KEYUP)
			{
				{
					//last_input->released = true;
					//printf("key released \n");
					switch(_event->key.keysym.sym)
					{
					case SDLK_LEFT:	{
						last_input->down[KEY_LEFT] = false;
						last_input->released[KEY_LEFT] = true;} break;

					case SDLK_RIGHT:{
						last_input->down[KEY_RIGHT] = false;
						last_input->released[KEY_RIGHT] = true;} break;

					case SDLK_UP:	{
						last_input->down[KEY_UP]= false;
						last_input->released[KEY_UP] = true;} break;

					case SDLK_DOWN:	{
						last_input->down[KEY_DOWN] = false;
						last_input->released[KEY_DOWN] = true;} break;

					case SDLK_SPACE:{
						last_input->down[KEY_SPACE] = false;
						last_input->released[KEY_SPACE] = true;} break;

					case SDLK_RETURN:{
						last_input->down[KEY_ENTER]= false;
						last_input->released[KEY_ENTER] = true;} break;

					case SDLK_ESCAPE:{
						last_input->down[KEY_EXIT] = false;
						last_input->released[KEY_EXIT] = true;} break;
					}
				}
			}
			else if (event_type == SDL_MOUSEBUTTONDOWN)
			{
				//last_input->pressed = true;
				switch((int)_event->button.button)
				{
				case SDL_BUTTON_LEFT : {
					last_input->down[KEY_MOUSE_LEFT] = true;
					last_input->pressed[KEY_MOUSE_LEFT] = true;} break;

				case SDL_BUTTON_RIGHT : {
					last_input->down[KEY_MOUSE_RIGHT] = true;
					last_input->pressed[KEY_MOUSE_RIGHT] = true;} break;

				case SDL_BUTTON_MIDDLE : {
					last_input->down[KEY_MOUSE_MIDDLE] = true;
					last_input->pressed[KEY_MOUSE_MIDDLE] = true;} break;

				}

			}
			else if (event_type == SDL_MOUSEBUTTONUP)
			{
				//last_input->released = true;
				//printf("mouse released \n");
				switch(_event->button.button)
				{
				case SDL_BUTTON_LEFT : {
					last_input->down[KEY_MOUSE_LEFT] = false;
					last_input->released[KEY_MOUSE_LEFT] = true;} break;

				case SDL_BUTTON_RIGHT : {
					last_input->down[KEY_MOUSE_RIGHT] = false;
					last_input->released[KEY_MOUSE_RIGHT] = true;} break;

				case SDL_BUTTON_MIDDLE : {
					last_input->down[KEY_MOUSE_MIDDLE] = false;
					last_input->released[KEY_MOUSE_MIDDLE] = true;} break;
				}
			}

		}
	}
	GameInput Result = GameInput();
	if (last_input){Result = *last_input;}
	return Result;
}


LOCAL_CONSTANT float MS_TO_SEC = 0.001f;
LOCAL_CONSTANT float SEC_TO_MS = 1000.0f;

//returns seconds eg 0.001 seconds
TIME_SEC_float game_dt(TickInfo * ti)
{
	TIME_SEC_float dt = ti->_ticksSinceLastFrame * MS_TO_SEC;
	return dt;
}

LOCAL_CONSTANT TIME_MS_int TARGET_FRAME_PERIOD = 40;
LOCAL_CONSTANT TIME_MS_int GAME_WAIT_TIME = 15;

std::list<int> fps_av_list;
std::list<int> fps_list;
LOCAL_CONSTANT int FPS_CHART_SAMPLES = 512;
//move SDL_GET_TICKS out to platform.h
void update_dt(TickInfo * ti,float current_dt, float dt_cap)
{

	//if (current_dt > dt_cap){ti->_ticksSinceLastFrame = (int)(dt_cap*SEC_TO_MS);}
	Uint32 cur_ticks = SDL_GetTicks();
	//while(SDL_GetTicks() - ti->last_frame_tick < TARGET_FRAME_PERIOD)
	//{
	//	SDL_Delay(GAME_WAIT_TIME);
	//}
	//cur_ticks = SDL_GetTicks();
	ti->_ticksSinceLastFrame =cur_ticks - ti->last_frame_tick;

	if (ti->_ticksSinceLastFrame < 1)
	{
		printf("Error : game dt = &i",ti->_ticksSinceLastFrame);
		ti->_ticksSinceLastFrame = 1;
	}

	if (!(cur_ticks != ti->last_frame_tick))
	{
		printf("tick error\n");
	}

	ti->last_frame_tick = cur_ticks;

	
	//fps_list.push_back(cur_tick1 - SDL_GetTicks());

	if (fps_av_list.size() >= FPS_CHART_SAMPLES){fps_av_list.pop_front();}
	//if ( fps_list.size() >= FPS_CHART_SAMPLES){fps_list.pop_front();}
}

//clean this function up... to many members of game used, parameterise this
void update_camera(POS_ABS_V2 & camera_pos, Entity * ent_camera_target,  TIME_SEC_float dt)
{
	assert(ent_camera_target);
	if(ent_camera_target)
	{
		Position camera_target_pos = *entity_get_position(ent_camera_target);
		V2 dif = math_subtract(camera_target_pos.world, 
			(camera_pos + v2(G::SCREEN_WIDTH/2,G::SCREEN_HEIGHT/2)));

		camera_pos  = camera_pos + v2(math_get_signf(dif.x),math_get_signf(dif.y))*dif*dif*dt*0.01f;
	}
}

Game :: Game()
{

}

Entity * _camera_target = 0;

void game_setup(Game * game )
{
	game->camera_pos.x = 0;
	game->camera_pos.y = 0;
	game->tick_info.last_frame_tick = 0;
	game->tick_info._ticksSinceLastFrame = 1;

	for (int i =0; i < G::CHUNK_COUNT; i++)
	{
		for (int j = 0;j < G::CHUNK_COUNT; j++)
		{
			game->_chunks[j][i] = 0;
		}
	}
	item_load();
	Chunk * init_chunk = game_get_chunk(game->_chunks, v2i(0,0));
	game->_message_router = new MessageRouter();
	game_chunk_init(game->_message_router);

	_camera_target = game_create_entity(game->_chunks, init_chunk, ENTITY_TYPE_PLAYER,v2(43,122),0,0);
	init_chunk->entitys->ConsolidateList();

	//hook up animations
#if 0
	MessageRouter_RegisterTriggerCallback(game->_message_router, entity_animation_listener, "on_move");
#endif 
}

//move graphics out of here
void update_game_graphics(Chunk * chunk_array[G::CHUNK_COUNT][G::CHUNK_COUNT],
							POS_ABS_V2 camera_pos, POS_ABS_V2i start_chunk, POS_ABS_V2i end_chunk, TickInfo * ti )
{
	graphics_start_render();
	graphics_render_tilemap(chunk_array, camera_pos, false);

	V2i cid = {0,0};
	for (cid.y = start_chunk.y; cid.y < end_chunk.y; cid.y++)
	for (cid.x = start_chunk.x; cid.x < end_chunk.x; cid.x++)
	{
		Chunk * cur_chunk = game_get_chunk(chunk_array, cid);	//get new chunk
		if (!cur_chunk){continue;}

		unsigned int entity_count = cur_chunk->entitys->size();
		for (unsigned int entity_index = 0; entity_index < entity_count; entity_index++)
		{
			Entity * cur_entity = cur_chunk->entitys->at(entity_index);

			graphics_render_entity(cur_entity->size, camera_pos, entity_get_position(cur_entity)->world,cur_entity->sprite_index,cur_entity->id%255);
		}
	}
	int fps = (int)(1.0f / game_dt(ti));
	char buf[10] = {};
	char * p = _itoa(fps,buf,10);
	//graphics_render_text(std::string(p));//+  //;
	fps_av_list.push_back(fps);
	//draw_fps_chart(camera_pos);
	graphics_render_stack(camera_pos);
	graphics_final_render();
}

//move sdl get ticks out
BOOL_QUIT game_run(Game * game)
{
	BOOL_QUIT Result = 0;
	float dt = game_dt(&game->tick_info);
	Chunk * cur_chunk = 0;
	V2i min_cid, max_cid, out_camera_cid = {};	//cid = chunk index

	get_chunks_on_screen(game->camera_pos,min_cid,max_cid,out_camera_cid);
	
	update_camera(game->camera_pos,_camera_target,dt); 
	update_dt(&game->tick_info,dt,G::GAME_DT_CAP);

	//move this out/ up
	update_game_graphics(game->_chunks, game->camera_pos, min_cid, max_cid, &game->tick_info);
	

	//can we check if cur_chunk is valid?

	V2i cid = {0,0};
	for (cid.y =min_cid.y; cid.y < max_cid.y; cid.y++)
	for (cid.x = min_cid.x; cid.x < max_cid.x; cid.x++)
	{
		assert(math_values_in_range(cid,v2i(0,0),v2i(G::CHUNK_COUNT)));

		if (cur_chunk)	{
			cur_chunk->entitys->ConsolidateList();//consolidate old chunk
		}

		cur_chunk = game_get_chunk(game->_chunks, cid);	//get new chunk

		if (!cur_chunk){continue;}

		std::vector<Entity*> * entity_list = cur_chunk->entitys->_vec;
		std::vector<Entity*>::iterator cur_entity;

		for(cur_entity = entity_list->begin();cur_entity!= entity_list->end(); ++cur_entity)
		{
			if (IsSet(*cur_entity,ENTITY_PLAYER))
			{
				*(*cur_entity)->input =
					game_input_get_buttons(&game->sdl_event, (*cur_entity)->input);

				if ((*cur_entity)->input->quit){Result = true;}
			}
			else
			{
				//ai update
			}

			entity_update(dt,game->_chunks, game->camera_pos, cur_chunk,*cur_entity);
		}


		/* 
		TODO:
		Remove this later, this is a hack to stop graphics from crit erroring, after rendering
		a removed entity...
		*/
		if (cur_chunk)	{
			cur_chunk->entitys->ConsolidateList();
		}
	}
	return Result;
}

